package com.daqian.chance.elastic.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.daqian.chance.core.exception.ElasticSearchException;
import com.daqian.chance.elastic.search.po.DocumentEntity;
import com.daqian.chance.elastic.search.po.ESEntity;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @Author: daqian
 * @Date: 2019/12/4 11:49
 */

@Slf4j
@Component
public class ElasticSearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    private final static String INDEX_INSERT_UPDATE_FAILED = "索引插入或更新失败";
    private final static String INDEX_DELETE_FAILED = "索引删除失败";
    private final static String DOCUMENT_INSERT_UPDATE_FAILED = "文档插入或更新失败";
    private final static String DOCUMENT_DELETE_FAILED = "文档删除失败";

    /**
     * 插入或更新一个document
     *
     * @param indexName
     * @param doc
     */
    public void insertOrUpdateDoc(String indexName, DocumentEntity doc) {
        UpdateRequest request = new UpdateRequest(indexName, doc.get_id());
        String docJsonString = JSON.toJSONString(doc);

        request.doc(docJsonString, XContentType.JSON);
        request.upsert(docJsonString, XContentType.JSON);
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        try {
            UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
            if (updateResponse.getId() == null) {
                throw new ElasticSearchException(INDEX_INSERT_UPDATE_FAILED);
            }
        } catch (Exception e) {
            log.error(INDEX_INSERT_UPDATE_FAILED, e);
            throw new ElasticSearchException(e.getMessage());
        }
    }

    public void deleteDoc(String indexName, String docId) {
        DeleteRequest request = new DeleteRequest(indexName, docId);
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        try {
            DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
            if (deleteResponse.getId() == null) {
                throw new ElasticSearchException(INDEX_DELETE_FAILED);
            }
        } catch (Exception e) {
            log.error("Failed to insertOrUpdateDoc: ", e);
            throw new ElasticSearchException(e.getMessage());
        }
    }


    /**
     * index是否存在
     *
     * @param indexName index名
     * @return boolean
     * @throws
     */
    public boolean indexExist(String indexName) throws Exception {
        GetIndexRequest request = new GetIndexRequest(indexName);
        request.local(false);
        request.humanReadable(true);
        request.includeDefaults(false);
        request.indicesOptions(IndicesOptions.lenientExpandOpen());
        return restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
    }


    /**
     * @param indexName index
     * @param entity    对象
     * @return void
     * @throws
     * @author WCNGS@QQ.COM
     * @See
     * @date 2019/10/17 17:27
     * @since
     */
    public void deleteOne(String indexName, ESEntity entity) {
        DeleteRequest request = new DeleteRequest(indexName);
        request.id(entity.getId());
        try {
            restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 批量插入数据
     *
     * @param indexName index
     * @param list      带插入列表
     * @return void
     * @throws
     * @author WCNGS@QQ.COM
     * @See
     * @date 2019/10/17 17:26
     * @since
     */
    public void insertBatch(String indexName, List<ESEntity> list) {
        BulkRequest request = new BulkRequest();
        list.forEach(item -> request.add(new IndexRequest(indexName).id(item.getId())
                .source(JSON.toJSONString(item.getData()), XContentType.JSON)));
        try {
            restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 批量插入数据
     *
     * @param indexName index
     * @param list      带插入列表
     * @return void
     * @throws
     * @author WCNGS@QQ.COM
     * @See
     * @date 2019/10/17 17:26
     * @since
     */
    public void insertBatchTrueObj(String indexName, List<ESEntity> list) {
        BulkRequest request = new BulkRequest();
        list.forEach(item -> request.add(new IndexRequest(indexName).id(item.getId())
                .source(item.getData(), XContentType.JSON)));
        try {
            restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 批量删除
     *
     * @param indexName index
     * @param idList    待删除列表
     * @return void
     * @throws
     * @author WCNGS@QQ.COM
     * @See
     * @date 2019/10/17 17:14
     * @since
     */
    public <T> void deleteBatch(String indexName, Collection<T> idList) {
        BulkRequest request = new BulkRequest();
        idList.forEach(item -> request.add(new DeleteRequest(indexName, item.toString())));
        try {
            restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param indexName index
     * @param builder   查询参数
     * @param c         结果类对象
     * @return java.util.List<T>
     * @throws
     * @author WCNGS@QQ.COM
     * @See
     * @date 2019/10/17 17:14
     * @since
     */
    public <T> List<T> search(String indexName, SearchSourceBuilder builder, Class<T> c) {
        SearchRequest request = new SearchRequest(indexName);
        request.source(builder);
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            List<T> res = new ArrayList<>(hits.length);
            for (SearchHit hit : hits) {
                res.add(JSON.parseObject(hit.getSourceAsString(), c));
            }
            return res;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除index
     *
     * @param indexName
     * @return void
     * @throws
     * @author WCNGS@QQ.COM
     * @See
     * @date 2019/10/17 17:13
     * @since
     */
    public void deleteIndex(String indexName) {
        try {
//            if (!this.indexExist(indexName)) {
//                log.error(" indexName={} 已经存在", indexName);
//                return;
//            }
            restHighLevelClient.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * @param indexName
     * @param builder
     * @return void
     * @throws
     * @author WCNGS@QQ.COM
     * @See
     * @date 2019/10/17 17:13
     * @since
     */
    public void deleteByQuery(String indexName, QueryBuilder builder) {

        DeleteByQueryRequest request = new DeleteByQueryRequest(indexName);
        request.setQuery(builder);
        //设置批量操作数量,最大为10000
        request.setBatchSize(10000);
        request.setConflicts("proceed");
        try {
            restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}